package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * @Classname DynamicRouteLoader
 * @Date 2024/12/9 20:43
 * @Created by 鲍宪立
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {


    private final NacosConfigManager config;
    private final RouteDefinitionWriter writer;

    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";

    private Set<String> routeIds = new HashSet<>();

    @PostConstruct//这个注解用于标记一个方法，这个方法将在Bean初始化完成后被执行
    public void initDynamicRoute() throws NacosException {
        //1.注册监听器并首次获取配置
        String configInfo = config.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                updateConfigInfo(configInfo);
            }});
        //2.首次启动时，更新一次配置
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo) {
        log.info("监听到的配置变更，{}", configInfo);
        //1.反序列化
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2.更新前先清空旧路由
        //2.1.清楚旧路由
        for (String routeId: routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();
          //2.2.判断是否有新的路由要更新
          if (routeDefinitions.isEmpty()) {
              //无路由配置，直接结束
              return;
          }
        //3.更新路由
        for (RouteDefinition routeDefinition: routeDefinitions) {
            //3.1更新路由
            writer.save(Mono.just(routeDefinition)).subscribe();
            //3.2记录路由id，方便将来的删除
            routeIds.add(routeDefinition.getId());
        }
    }
}
